package com.rybbaby.tss.tasks.core.utils;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.net.ssl.HttpsURLConnection;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

/**
 * @description Http 请求工具类
 * 
 * @author 谢进伟
 * 
 * @createDate 2016年9月20日 下午5:26:47
 */
public class HttpUtil {
	
	private static final String ENCODING = "UTF-8";
	private static Logger log = Logger.getLogger(HttpUtil.class);
	
	/**
	 * 发起远程Http请求
	 * 
	 * @param httpUrl
	 *            请求地址
	 * @param paramsStr
	 *            参数字符串 格式：paramNmae1=paramValue1 & paramName2=paramValue2...
	 * @return
	 * @throws Exception
	 */
	public static String sendHttpsRemoteRequest(String httpUrl , RequestMethod method , String paramsStr) {
		return sendRemoteRequest(httpUrl , true , method , paramsStr);
	}
	
	/**
	 * 发起远程Http请求
	 * 
	 * @param httpUrl
	 *            请求地址
	 * @param paramsStr
	 *            参数字符串 格式：paramNmae1=paramValue1 & paramName2=paramValue2...
	 * @return
	 * @throws Exception
	 */
	public static String sendHttpRemoteRequest(String httpUrl , RequestMethod method , String paramsStr) {
		return sendRemoteRequest(httpUrl , false , method , paramsStr);
	}
	
	/**
	 * 发起远程 Https 形式的get请求请求
	 * 
	 * @param url
	 *            请求地址
	 * @param params
	 *            参数
	 * @return
	 */
	public static String httpsGet(String url , Map<String , String> params) {
		return sendHttpsRemoteRequest(url , RequestMethod.GET , switchParamsModel(params));
	}
	
	/**
	 * 发起远程 Https 形式的pst请求请求
	 * 
	 * @param url
	 *            请求地址
	 * @param params
	 *            参数
	 * @return
	 */
	public static String httpsPost(String url , Map<String , String> params) {
		return sendHttpsRemoteRequest(url , RequestMethod.POST , switchParamsModel(params));
	}
	
	/**
	 * 发送 http 形式的get请求
	 * 
	 * @param url
	 *            请求路径
	 * @param params
	 *            参数
	 * @param returnType
	 *            请求结果返回形式
	 * @return
	 * @throws HttpException
	 * @throws IOException
	 */
	public static Object httpGet(String url , Map<String , String> params , ReturnType returnType) throws HttpException , IOException {
		GetMethod method = new GetMethod(url);
		HttpClientParams paraml = new HttpClientParams();
		if(params != null && !params.isEmpty()) {// 设置参数
			NameValuePair [] NameValuePairs = new NameValuePair [params.size()];
			int index = 0;
			Set<String> keys = params.keySet();
			for(String parameterName : keys) {
				String parameterValue = params.get(parameterName);
				NameValuePairs[index++] = new NameValuePair(parameterName , parameterValue);
			}
			method.setQueryString(NameValuePairs);
		}
		HttpClient hc = new HttpClient(paraml);
		hc.executeMethod(method);
		if(returnType == null || (returnType != null && returnType == ReturnType.String)) {
			return method.getResponseBodyAsString();
		} else if(returnType == ReturnType.byteArray) {
			return method.getResponseBody();
		} else {
			return method.getResponseBodyAsStream();
		}
	}
	
	/**
	 * 发送 http 形式的post请求
	 * 
	 * @param url
	 *            请求路径
	 * @param params
	 *            参数
	 * @param returnType
	 *            请求结果返回形式
	 * @return
	 * @throws HttpException
	 * @throws IOException
	 */
	public static Object httpPost(String url , Map<String , String> params , ReturnType returnType) throws HttpException , IOException {
		PostMethod method = new PostMethod(url);
		if(params != null && !params.isEmpty()) {
			Set<String> keys = params.keySet();
			for(String parameterName : keys) {
				String parameterValue = params.get(parameterName);
				method.addParameter(parameterName , parameterValue);
			}
		}
		HttpClient hc = new HttpClient();
		hc.executeMethod(method);
		if(returnType == null || (returnType != null && returnType == ReturnType.String)) {
			return method.getResponseBodyAsString();
		} else if(returnType == ReturnType.byteArray) {
			return method.getResponseBody();
		} else {
			return method.getResponseBodyAsStream();
		}
	}
	
	/**
	 * 模拟上传文件(远程URL)
	 * 
	 * @Title uploadFile
	 * @param uploadUrl
	 *            上传路径
	 * @param picUrl
	 *            远程图片URL
	 * @param params
	 *            参数内容
	 * @return
	 * @throws IOException
	 * @author zh
	 * @date 2016年6月30日 下午4:22:10
	 */
	public static String uploadFile(String uploadUrl , URL picUrl , Map<String , String> params) throws IOException {
		return uploadFile(uploadUrl , picUrl.openStream() , picUrl.getFile().substring(picUrl.getFile().lastIndexOf("/")) + 1 , params);
	}
	
	/**
	 * 模拟上传文件(本地文件)
	 * 
	 * @Title uploadFile
	 * @param uploadUrl
	 *            上传路径
	 * @param file
	 *            本地文件
	 * @param params
	 *            参数内容
	 * @return
	 */
	public static String uploadFile(String uploadUrl , File file , Map<String , String> params) throws IOException {
		List<File> files = new ArrayList<File>();
		files.add(file);
		return uploadFile(uploadUrl , files , params);
	}
	
	/**
	 * 模拟上传文件，支持多文件上传(本地文件)
	 *
	 * @Title uploadFile
	 * @param uploadUrl
	 *            上传路径
	 * @param files
	 *            本地文件列表
	 * @param params
	 *            参数内容
	 * @return
	 */
	public static String uploadFile(String uploadUrl , List<File> files , Map<String , String> params) throws IOException {
		PostMethod method = new PostMethod(uploadUrl);
		boolean includeFile = files != null && !files.isEmpty();
		boolean hasParams = params != null && !params.isEmpty();
		int length = (includeFile ? files.size() : 0) + (hasParams ? params.size() : 0);
		Part [] parts = new Part [length];
		int index = 0;// 参数下标
		if(hasParams) {
			// 设置出文件参数以外的参数
			parts = new Part [params.size() + 1];
			Set<String> keys = params.keySet();
			for(String key : keys) {
				String value = params.get(key);
				parts[index++] = new StringPart(key , value);
			}
		}
		if(includeFile) {
			// 设置文件参数
			for(File file : files) {
				FilePart filePart = new FilePart(file.getName() , file);
				parts[index++] = filePart;
			}
		}
		RequestEntity requestEntity = new MultipartRequestEntity(parts , method.getParams());
		method.setRequestEntity(requestEntity);
		HttpClient hc = new HttpClient();
		hc.executeMethod(method);
		String result = method.getResponseBodyAsString();
		return result;
	}
	
	/**
	 * 模拟上传文件（输入流）
	 * 
	 * @Title uploadFile
	 * @param uploadUrl
	 *            上传链接
	 * @param is
	 *            输入流
	 * @param filename
	 *            文件名
	 * @param params
	 *            参数
	 * @return
	 */
	public static String uploadFile(String uploadUrl , InputStream is , String filename , Map<String , String> params) throws IOException {
		StringBuffer responseStr = new StringBuffer();
		DataInputStream fs = null;
		OutputStream os = null;
		DataOutputStream dos = null;
		ByteArrayOutputStream baos = null;
		try {
			URL urlGet = new URL(uploadUrl);
			HttpURLConnection conn = (HttpURLConnection)urlGet.openConnection();
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("connection" , "Keep-Alive");
			conn.setRequestProperty("user-agent" , "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
			conn.setRequestProperty("Charsert" , "UTF-8");
			// 定义数据分隔线
			String BOUNDARY = "-------" + System.nanoTime() + "-------";
			conn.setRequestProperty("Content-Type" , "multipart/form-data; boundary=" + BOUNDARY);
			os = conn.getOutputStream();
			dos = new DataOutputStream(os);
			// 文件内容
			StringBuilder contentBody = new StringBuilder();
			String end = "\r\n";
			contentBody.append("--").append(BOUNDARY).append(end);
			contentBody.append("Content-Disposition: form-data;name=\"media\";filename=\"" + filename + "\"").append(end);
			contentBody.append("Content-Type:application/octet-stream").append(end).append(end);
			dos.write(contentBody.toString().getBytes());
			fs = new DataInputStream(is);
			int bytes = 0;
			byte [] bufferOut = new byte [1024];
			while ((bytes = fs.read(bufferOut)) != -1) {
				dos.write(bufferOut , 0 , bytes);
			}
			// 参数内容
			if(params != null && params.size() > 0) {
				for(String key : params.keySet()) {
					StringBuilder paramData = new StringBuilder();
					paramData.append(end).append("--").append(BOUNDARY).append(end);
					paramData.append("Content-Disposition: form-data;name=\"" + key + "\";");
					paramData.append(end);
					paramData.append(end);
					paramData.append("\"" + params.get(key) + "\"");
					dos.write(paramData.toString().getBytes());
				}
			}
			// 最后一个片段结尾要用--表示
			byte [] end_data = (end + "--" + BOUNDARY + "--" + end).getBytes();
			dos.write(end_data);
			dos.flush();
			is = conn.getInputStream();
			baos = getRemoteResponse(is);
			responseStr.append(new String(baos.toByteArray() , ENCODING));
			if(conn != null) {
				conn.disconnect();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(dos);
			IOUtils.closeQuietly(fs);
		}
		return responseStr.toString();
	}
	
	/**
	 * 上传微信临时素材文件
	 * 
	 * @param file
	 *            需要上传到的媒体文件
	 * @param type
	 *            媒体文件类型，分别有图片（image）、语音（voice）、视频（video）、普通文件(file)、缩略图（thumb）
	 * 
	 * @return
	 *         <pre>
	 * {
	 *    "type": "image",
	 *    "media_id": "1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0",
	 *    "created_at": "1380000000"
	 * }
	 * </pre>
	 */
	public static String uploadTemporaryMediaFile(String url , File file , String type) {
		try {
			return uploadFile(url , file , null);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 下载远程文件
	 * 
	 * @param httpUrl
	 *            远程该文件地址
	 * @return
	 */
	public static RemoteFile downWechatRemoteFile(String httpUrl) {
		log.info("开始下载远程文件：" + httpUrl);
		RemoteFile rf = new RemoteFile();
		File tempFile = null;
		InputStream is = null;
		ByteArrayOutputStream baos = null;
		String fileName = null;
		OutputStream out = null;
		try {
			URL url = new URL(httpUrl);
			HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
			Map<String , List<String>> headerFields = conn.getHeaderFields();
			List<String> contentDisposition = headerFields.get("Content-disposition");
			if(contentDisposition != null && !contentDisposition.isEmpty()) {
				String contentDispositionValue = contentDisposition.get(0);
				fileName = StringUtils.substringBetween(contentDispositionValue , "attachment; filename=\"" , "\"");
				String [] fileNameArr = StringUtils.split(fileName , ".");
				tempFile = File.createTempFile(fileNameArr[0] , "." + fileNameArr[1]);
			}
			is = conn.getInputStream();
			baos = HttpUtil.getRemoteResponse(is);
			List<String> contentType = headerFields.get("Content-Type");
			if(contentType != null && !contentType.isEmpty()) {
				if(contentType.get(0).contains("application/json")) {
					String responseStr = new String(baos.toByteArray() , ENCODING);
					JSONObject json = JSONObject.fromObject(responseStr);
					rf.setErrorCode(json.getString("errcode"));
					rf.setErrorMsg(json.getString("errmsg"));
					log.info(responseStr);
				} else {
					out = new FileOutputStream(tempFile);
					baos.writeTo(out);
					rf.setFile(tempFile);
				}
			}
			if(conn != null) {
				conn.disconnect();
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(is);
			IOUtils.closeQuietly(out);
			IOUtils.closeQuietly(baos);
		}
		if(rf.getFile() != null) {
			log.info("下载成功!");
		} else {
			log.info("下载失败!");
		}
		return rf;
	}
	
	/**
	 * 读取输入流中字节数据
	 * 
	 * @param is
	 *            输入流
	 * @return
	 * @throws IOException
	 */
	public static ByteArrayOutputStream getRemoteResponse(InputStream is) throws IOException {
		ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
		byte [] b = new byte [100];
		int len = 0;
		while ((len = is.read(b , 0 , 100)) > 0) {
			swapStream.write(b , 0 , len);
		}
		swapStream.close();
		return swapStream;
	}
	
	/**
	 * 将map形式的参数转换成:参数名=参数值的形式
	 * 
	 * @param params
	 *            参数映射
	 * @return
	 */
	private static String switchParamsModel(Map<String , String> params) {
		List<String> paramsList = new ArrayList<String>();
		if(params != null && !params.isEmpty()) {
			Set<String> keys = params.keySet();
			for(String parameterName : keys) {
				String parameterValue = params.get(parameterName);
				paramsList.add(parameterName + "=" + parameterValue);
			}
		}
		return StringUtils.join(paramsList , "&");
	}
	
	/**
	 * 发起远程Http/https请求
	 * 
	 * @param httpUrl
	 *            请求地址
	 * @param useHttps
	 *            是否使用https请求方式
	 * @param paramsStr
	 *            参数字符串 格式：paramNmae1=paramValue1 & paramName2=paramValue2...
	 * @return
	 * @throws Exception
	 */
	private static String sendRemoteRequest(String httpUrl , boolean useHttps , RequestMethod method , String paramsStr) {
		StringBuffer responseStr = new StringBuffer();
		responseStr = new StringBuffer();
		InputStream is = null;
		ByteArrayOutputStream baos = null;
		try {
			boolean includeParams = StringUtils.isNotBlank(paramsStr);
			if(includeParams && method == RequestMethod.GET) {
				if(httpUrl.contains("?")) {
					httpUrl += "&" + paramsStr;
				} else {
					httpUrl += "?" + paramsStr;
				}
			}
			URL url = new URL(httpUrl);
			URLConnection conn = null;
			HttpsURLConnection httpsConn = null;
			HttpURLConnection httpConn = null;
			if(useHttps) {// 使用https形式
				httpsConn = (HttpsURLConnection)url.openConnection();
				conn = httpsConn;
			} else {// 普通http形式
				httpConn = (HttpURLConnection)url.openConnection();
				conn = httpConn;
			}
			conn.setConnectTimeout(600 * 1000);
			boolean isPost = method == RequestMethod.POST;
			if(isPost) {
				conn.setDoInput(true);
			}
			conn.setDoOutput(true);
			if(includeParams && isPost) {
				conn.getOutputStream().write(paramsStr.getBytes());
			}
			is = conn.getInputStream();
			baos = getRemoteResponse(is);
			responseStr.append(new String(baos.toByteArray() , ENCODING));
			if(httpsConn != null && conn instanceof HttpsURLConnection) {
				httpsConn.disconnect();
			} else if(httpConn != null && conn instanceof HttpURLConnection) {
				httpConn.disconnect();
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(is);
			IOUtils.closeQuietly(baos);
		}
		return responseStr.toString();
	}
	
	public static enum ReturnType {
		/** 直接数组 **/
		byteArray,
		/** 字符串 **/
		String,
		/** 流 **/
		Stream
	}
	
	public static enum RequestMethod {
		POST, GET
	}
}
